`timescale 1 ns / 1 ps
module tb ();

  parameter PERIOD = 20 ;
  parameter LENGTH = 10'd33 ;

  reg      pll_rst;
  reg      test_rst;
  wire   	tx_rx;
  wire		clk_10M;
  wire		pll_locked;

  wire     rst=!pll_locked;

  wire		[1:0]	 tx_rdy;

  reg		[15:0] tx_init_data;
  reg      [15:0] tx_data;
  reg		[9:0]  frame_len;//frame length
  reg		[4:0]  frame_st;
  reg             frame_end;

  reg				 tx_nce;
  reg				 tx_nwe;
  reg				 tx_noe;
  reg	  [8:0]   tx_rd_addr;
  reg             tx_rd_ab;

  wire	  [15:0]  tx_rd_data;
  reg	  [15:0]  tx_rd_data_r;

  wire    [9:0]   tx_rd_addr_ab=  {tx_rd_ab,tx_rd_addr};

  reg inclk ;

  initial
  begin
    inclk = 1'b0;
    #(PERIOD/2);
    forever
      #(PERIOD/2) inclk = ~inclk;
  end

  reg test_clk ;

  initial
  begin
    test_clk = 1'b1;
    #6 ;
    test_clk = 1'b0;
    #(PERIOD/3);
    forever
      #(PERIOD/3) test_clk = ~test_clk;
  end

  initial
  begin
    test_rst=1'b1;
    pll_rst=1'b1;
    tx_data=0;
    #50 pll_rst=1'b0;
    test_rst=1'b0;
  end

  reg					tx_en;  //tx_ena
  reg					rx_en;  //
  reg					tx_ack;
  reg					rx_ack;

  reg	[5:0]			cpu_high_addr;
  reg 	[2:0]			schedule_st;

  always@(posedge test_clk or posedge test_rst)
    if(rst)
    begin
      tx_en<=1'b0;
      rx_en<=1'b0;
      cpu_high_addr<=6'd0;
      schedule_st<=0;
    end
    else
    case(schedule_st)
      0:
      begin
        tx_en<=1'b1;
        cpu_high_addr<=6'd0; //0-1023
        schedule_st<=1;
      end
      1:
      begin
        //tx_en<=1'b1;
        if(tx_ack)
        begin
          tx_en<=1'b0;
          schedule_st<=2;
        end
      end
      2:
      begin
        cpu_high_addr<=6'd1;  //1024-2047
        rx_en<=1'b1;
        schedule_st<=3;
      end
      3:
      begin
        if(rx_ack)
        begin
          rx_en<=1'b0;
          schedule_st<=0;
        end
      end
      default:
        schedule_st<=0;
    endcase

  always@(posedge test_clk or posedge test_rst)
    if(rst)
    begin
      tx_rd_ab	  	<=1'b0;
      tx_rd_addr 	<=511;
      tx_nce	  	<=1'b1;
      tx_noe	  	<=1'b1;
      tx_nwe	  	<=1'b1;
      frame_len  	<=0;
      tx_data	  	<=0;
      frame_end  	<=1'b0;
      tx_ack	  	<=1'b0;
      tx_init_data<=0;
      tx_rd_data_r<=0;
      frame_st	  	<=0;  //frame state machine
    end
    else
    case(frame_st)
      0:
      begin
        tx_rd_ab	   <=1'b0;
        tx_rd_addr  <=511;
        tx_nce		<=1'b1;
        tx_noe		<=1'b1;
        tx_nwe		<=1'b1;
        tx_data		<=0;
        frame_end	<=1'b0;
        tx_ack	  	<=1'b0;
        tx_init_data<=0;
        tx_rd_data_r<=0;
        frame_st		<=1;
      end
      1:
      begin
        tx_nce		<=1'b1;
        tx_noe		<=1'b1;
        tx_nwe		<=1'b1;
        tx_rd_addr  <=511;
        tx_rd_data_r<=0;
        if(tx_en)
          frame_st		<=2;
      end
      2:
      begin  //start 1
        tx_nce		<=1'b0;
        tx_noe		<=1'b1;
        frame_st		<=3;
      end
      3:
      begin
        tx_nce		<=1'b0;
        tx_noe		<=1'b0;
        frame_st		<=4;
      end

      4:
      begin
        tx_nce		<=1'b0;
        tx_noe		<=1'b0;
        frame_st		<=5;
      end
      5:
      begin
        tx_nce		<=1'b0;
        tx_noe		<=1'b0;
        frame_st		<=6;
      end
      6:
      begin
        tx_nce		<=1'b0;
        tx_noe		<=1'b0;
        frame_st		<=7;
      end
      7:
      begin
        tx_nce		<=1'b0;
        tx_noe		<=1'b0;
        frame_st		<=8;
      end
      8:
      begin
        tx_nce		<=1'b0;
        tx_noe		<=1'b1;
        tx_rd_data_r<=tx_rd_data;
        frame_st		<=9;
      end
      9:
      begin  //start 1
        tx_nce		<=1'b1;
        tx_noe		<=1'b1;
        if(tx_rd_data_r[15])
        begin
          frame_st		<=10;
        end
        else
        begin
          frame_st		<=11;
        end
      end
      10:
      begin
        tx_nce		<=1'b1;
        tx_noe		<=1'b1;
        frame_st		<=22;
      end
      11:
      begin
        tx_nce		<=1'b1;
        tx_noe		<=1'b1;
        frame_st		<=12;
      end
      12:
      begin  //start 1
        frame_len	<=frame_len+2'd2;
        tx_nce		<=1'b0;
        tx_nwe		<=1'b1;
        tx_rd_addr  <=0;
        frame_st		<=14;
        tx_data		<=tx_init_data;
      end
      13:
      begin
        frame_len	<=frame_len+2;
        tx_data		<=tx_data+1;
        tx_rd_addr  <=tx_rd_addr+1'b1;
        tx_nce		<=1'b0;
        tx_nwe		<=1'b1;
        frame_st		<=14;
        if(frame_len>=LENGTH)
        begin
          frame_end<=1'b1;
          tx_data		<={1'b1,5'b0,LENGTH};
          tx_rd_addr  <=14'd511;
        end
      end
      14:
      begin
        frame_st		<=15;
        tx_nce		<=1'b0;
        tx_nwe		<=1'b0;
      end
      15:
      begin
        frame_st		<=16;
        tx_nce		<=1'b0;
        tx_nwe		<=1'b0;
      end
      16:
      begin
        frame_st		<=17;
        tx_nce		<=1'b0;
        tx_nwe		<=1'b0;
      end
      17:
      begin
        frame_st		<=18;
        tx_nce		<=1'b0;
        tx_nwe		<=1'b0;
      end
      18:
      begin
        frame_st		<=19;
        tx_nce		<=1'b0;
        tx_nwe		<=1'b1;
      end
      19:
      begin
        frame_st		<=20;
        tx_nce		<=1'b1;
        tx_nwe		<=1'b1;
      end
      20:
      begin
        frame_st		<=21;
        tx_nce		<=1'b1;
        tx_nwe		<=1'b1;
      end
      21:
      begin
        if(frame_end)
        begin
          frame_st		<=22;
          frame_len		<=0;
          tx_init_data<=tx_init_data+1'b1;
          tx_rd_ab	   <=~tx_rd_ab;
          tx_rd_addr   <=14'd511;
        end
        else
          frame_st		<=13;
      end
      22:
      begin
        frame_end	<=1'b0;
        tx_ack		<=1'b1;
        frame_st	<=23;
      end
      23:
      begin
        tx_ack		<=1'b0;
        frame_st	<=1;
      end
      default:
        frame_st<=0;
    endcase

  reg				 rx_nce;
  reg				 rx_noe;
  reg				 rx_nwe;
  wire		[15:0] rx_data;
  reg		[15:0] rx_data_cpu;
  reg		[15:0] rx_data_out;
  wire				 rx_rdy;
  reg		[8:0]  rx_addr_r;
  reg				 rx_toggle;
  reg		[9:0]  rx_len;
  reg      [4:0]  recv_st;

  wire		[9:0] rx_addr={rx_toggle,rx_addr_r};

  always@(posedge test_clk or posedge test_rst)
    if(test_rst)
    begin
      rx_nce		<=1'b1;
      rx_noe		<=1'b1;
      rx_nwe		<=1'b1;
      rx_toggle	<=1'b0;
      rx_addr_r	<=511;
      rx_len		<=0;
      rx_data_out	<=0;
      rx_ack		<=0;
      rx_data_cpu	<=0;
      recv_st		<=0;
    end

    else
    case(recv_st)
      0:
      begin
        rx_nce		<=1'b1;
        rx_noe		<=1'b1;
        rx_nwe		<=1'b1;
        rx_toggle	<=1'b0;
        rx_addr_r	<=511;
        rx_len		<=0;
        rx_data_out	<=0;
        rx_ack		<=0;
        rx_data_cpu	<=0;
        recv_st	<=1;
      end

      1:
      begin
        rx_nce		<=1'b1;
        rx_noe		<=1'b1;
        rx_nwe		<=1'b1;
        rx_len		<=0;
        rx_ack		<=0;
        rx_data_cpu	<=0;
        if(rx_en)
        begin
          rx_nce	<=1'b0;
          recv_st	<=2;
        end
      end

      2:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        rx_len	<=0;
        recv_st	<=3;
      end
      3:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        rx_len	<=0;
        recv_st	<=4;
      end
      4:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        rx_len	<=0;
        recv_st	<=5;
      end
      5:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        rx_len	<=0;
        recv_st	<=6;
      end
      6:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        rx_len	<=0;
        recv_st	<=7;
      end
      7:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b1;
        rx_nwe	<=1'b1;
        rx_data_cpu	<=rx_data;
        rx_len	<=0;
        recv_st	<=8;
      end
      8:
      begin
        rx_nce	<=1'b1;
        rx_noe	<=1'b1;
        rx_nwe	<=1'b1;
        rx_len	<=0;
        recv_st	<=9;
      end
      9:
      begin
        rx_nce	<=1'b1;
        rx_noe	<=1'b1;
        rx_nwe	<=1'b1;
        rx_len	<=0;
        recv_st	<=10;
      end
      10:
      begin
        rx_nce	<=1'b1;
        rx_noe	<=1'b1;
        rx_nwe	<=1'b1;
        if(rx_data_cpu[15])
        begin
          rx_len	<=rx_data_cpu[9:0];
          rx_addr_r	<=rx_addr_r+1;
          recv_st	<=11;
        end
        else
        begin
          rx_addr_r	<=511;
          recv_st		<=27;
        end
      end
      11:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b1;
        rx_nwe	<=1'b1;
        recv_st	<=12;
      end
      12:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        recv_st	<=13;
      end
      13:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        recv_st	<=14;
      end
      14:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;

        recv_st	<=15;
      end
      15:
      begin
        rx_nce	<=1'b0;
        rx_noe	<=1'b0;
        rx_nwe	<=1'b1;
        recv_st	<=16;
      end
      16:
      begin
        rx_nce		<=1'b0;
        rx_noe		<=1'b1;
        rx_nwe		<=1'b1;
        rx_data_cpu	<=rx_data;
        recv_st		<=17;
      end
      17:
      begin
        rx_nce		<=1'b1;
        rx_noe		<=1'b1;
        rx_nwe		<=1'b1;
        recv_st		<=18;
      end
      18:
      begin
        rx_nce		<=1'b1;
        rx_noe		<=1'b1;
        rx_nwe		<=1'b1;
        if(rx_len==1)
          rx_len		<=0;
        else
          rx_len		<=rx_len-2'd2;
        rx_data_out	<=rx_data_cpu;
        recv_st		<=19;
      end
      19:
      begin
        rx_nce		<=1'b1;
        rx_noe		<=1'b1;
        rx_nwe		<=1'b1;
        rx_addr_r<=rx_addr_r+1;
        if(rx_len==0)
        begin
          rx_addr_r	<=511;
          recv_st		<=20;
        end
        else
          recv_st		<=11;
      end
      20:
      begin
        rx_nce		<=1'b0;
        rx_nwe		<=1'b0;
        recv_st		<=21;
      end
      21:
      begin
        rx_nce		<=1'b0;
        rx_nwe		<=1'b0;
        recv_st		<=22;
      end

      22:
      begin
        rx_nce		<=1'b0;
        rx_nwe		<=1'b0;
        recv_st		<=23;
      end
      23:
      begin
        rx_nce		<=1'b0;
        rx_nwe		<=1'b0;
        recv_st		<=24;
      end
      24:
      begin
        rx_nce		<=1'b0;
        rx_nwe		<=1'b1;
        recv_st		<=25;
      end
      25:
      begin
        rx_nce		<=1'b1;
        rx_nwe		<=1'b1;
        recv_st		<=26;
      end
      26:
      begin
        rx_nce		<=1'b1;
        rx_nwe		<=1'b1;
        rx_toggle<= ~rx_toggle;
        recv_st		<=27;
      end
      27:
      begin
        //if(rx_rdy) begin
        rx_ack<=1'b1;
        recv_st	<=28;
      end
      28:
      begin
        //if(rx_rdy) begin
        rx_ack<=1'b0;
        recv_st	<=1;
      end
      default:
        recv_st	<=0;
    endcase

  wire [15:0]   cpu_addr;
  wire 			  cpu_nce;
  wire			  cpu_noe;
  wire			  cpu_nwe;
  wire [15:0]   cpu_data;

  assign		  cpu_addr =tx_en?{cpu_high_addr,tx_rd_addr_ab}:

            (rx_en?{cpu_high_addr,rx_addr}:16'hZZZZ);

  assign		  cpu_nce 	=tx_en?tx_nce:(rx_en?rx_nce:1'b1);
  assign		  cpu_noe 	=tx_en?tx_noe:(rx_en?rx_noe:1'bZ);
  assign        cpu_nwe 	=tx_en?tx_nwe:(rx_en?rx_nwe:1'bZ);
  assign		  cpu_data	=tx_en?((~tx_nwe)?tx_data:16'hZZZZ):
            (rx_en?((~rx_nwe)?16'h0000:16'hZZZZ):16'hZZZZ);
  assign		  tx_rd_data=(tx_en&(~cpu_noe))?cpu_data:16'h0000;
  assign		  rx_data	=(rx_en&(~cpu_noe))?cpu_data:16'h0000;

  lvds_top   lvds_top_inst
             (
               .inclk			(inclk),
               .pll_rst			(pll_rst),
               .cpu_nce			(cpu_nce),
               .cpu_noe			(cpu_noe),
               .cpu_nwe			(cpu_nwe),
               .cpu_addr		(cpu_addr),
               .cpu_data		(cpu_data),
               .tx_rdy			(tx_rdy),
               .tx				(tx_rx),
               .rx				(tx_rx),
               .clk_10M			(clk_10M),
               .pll_locked		(pll_locked)
             );

endmodule

